package com.spring.framework.context;
import com.spring.framework.annotation.MyAutowired;
import com.spring.framework.annotation.MyController;
import com.spring.framework.annotation.MyService;
import com.spring.framework.aop.MyJdkDynamicAopProxy;
import com.spring.framework.aop.config.MyAopConfig;
import com.spring.framework.aop.support.MyAdviceSupport;
import com.spring.framework.beans.MyBeanWrapper;
import com.spring.framework.beans.config.MyBeanDefinition;
import com.spring.framework.beans.support.MyBeanDefinitionReader;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @author ππ
 * @date 2020-11-16 16:54
 * @desc 简单的理解为工厂类,只是完成IoC和DI
 */
public class MyApplicationContext {
    /**声明解析器，根据配置文件*/
    private MyBeanDefinitionReader reader;
    /**存储BeanDefinition*/
    private Map<String,MyBeanDefinition> beanDefinitionMap = new HashMap<>();
    /**缓存包装类BeanWrapper*/
    private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<>();
    /**缓存源生Bean*/
    private Map<String,Object> factoryBeanObjectCache = new HashMap<>();
    /**缓存support*/
    private Map<String,MyAdviceSupport> adviceSupportMap = new HashMap<>();
    public MyApplicationContext(String... configLocations) {
        try {
            //1、由DefinitionReader进行配置文件解析，并封装成BeanDefinition集合
            reader = new MyBeanDefinitionReader(configLocations);
            //2、加载配置文件，并封装成BeanDefinition集合
            List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
            //3、将BeanDefinition保存到内存中
            doRegistryBeanDefinition(beanDefinitions);
            //4、执行DI操作。初始化非Lazy、单例
            doAutowired();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Object getBean(Class<?> clazz) {
        return getBean(clazz.getName());
    }
    /**
     * 根据beanName获取bean实例
     * 可能出现循环依赖问题
     * @param beanName
     * @return
     */
    public Object getBean(String beanName) {
        //1、拿到配置信息
        MyBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            return null;
        }
        //2、实例化
        Object instance = instantiateBean(beanName,beanDefinition);
        //3、封装成BeanWrapper
        MyBeanWrapper beanWrapper = instantiateWrapper(beanName,instance);
        //4、执行DI操作
        populateBean(beanName,beanDefinition,beanWrapper);
        return beanWrapper.getWrapperInstance();
    }
    private void populateBean(String beanName, MyBeanDefinition beanDefinition, MyBeanWrapper beanWrapper) {
        // 可能存在循环依赖的问题
        Class<?> clazz = beanWrapper.getWrapperClass();
        Object instance = beanWrapper.getWrapperInstance();
        // 对于本例而言，@MyController和@MyService注解的判断可以取消，因为扫描时只处理了这两种情况
        if(!(clazz.isAnnotationPresent(MyController.class)||clazz.isAnnotationPresent(MyService.class))){
            return;
        }
        for(Field field : clazz.getDeclaredFields()) {
            if (!field.isAnnotationPresent(MyAutowired.class)) {
                continue;
            }
            String autowiredBeanName = field.getAnnotation(MyAutowired.class).value().trim();
            if ("".equals(autowiredBeanName)) {
               autowiredBeanName = field.getType().getName();
            }
            // 设置强制访问
            field.setAccessible(true);
            if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                return;
            }
            try {
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    private void doAutowired() {
        if (this.beanDefinitionMap.isEmpty()) {
            return;
        }
        for (Map.Entry<String,MyBeanDefinition> entry : this.beanDefinitionMap.entrySet()) {
            getBean(entry.getKey());
        }
    }

    private void doRegistryBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception {
        if (beanDefinitions.isEmpty()) {
            return;
        }
        for (MyBeanDefinition beanDefinition : beanDefinitions) {
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + " has already exist!!");
            }
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(),beanDefinition);
        }
    }
    private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {
        Object instance = null;
        if (this.factoryBeanObjectCache.get(beanName) != null) {
            instance = this.factoryBeanObjectCache.get(beanName);
        } else {
            try {
                instance = beanDefinition.getBeanClass().newInstance();
                //========================AOP开始=========================================
                //生成Support对象，理论上应该采用容器式单例？源码验证
                MyAdviceSupport config = instantiateAopConfig(beanDefinition,instance);
                if(config.pointCutMatch()){
                    instance = new MyJdkDynamicAopProxy(config).getProxy();
                }
                //========================AOP结束=========================================

                this.factoryBeanObjectCache.put(beanName,instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return instance;
    }

    /**
     * 构建AOP支持类
     * @param beanDefinition
     * @param instance
     * @return
     */
    private MyAdviceSupport instantiateAopConfig(MyBeanDefinition beanDefinition, Object instance) {
        MyAdviceSupport support;
        if(this.adviceSupportMap.get(beanDefinition.getBeanClassName())!=null) {
            support = this.adviceSupportMap.get(beanDefinition.getBeanClassName());
        } else {
            // 若属性多，可采用原型模式
            MyAopConfig aopConfig = new MyAopConfig();
            aopConfig.setPointCut(this.reader.getConfig().getProperty("pointCut"));
            aopConfig.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
            aopConfig.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
            aopConfig.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
            aopConfig.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
            aopConfig.setAspectAfterThrowName(this.reader.getConfig().getProperty("aspectAfterThrowName"));
            support = new MyAdviceSupport(aopConfig);
            support.setTargetClass(beanDefinition.getBeanClass());
            support.setTargetInstance(instance);
            this.adviceSupportMap.put(beanDefinition.getBeanClassName(),support);
        }

        return support;
    }

    private MyBeanWrapper instantiateWrapper(String beanName,Object instance) {
        MyBeanWrapper beanWrapper;
        if(this.factoryBeanInstanceCache.containsKey(beanName)) {
            beanWrapper = this.factoryBeanInstanceCache.get(beanName);
        } else {
            beanWrapper = new MyBeanWrapper(instance);
            this.factoryBeanInstanceCache.put(beanName,beanWrapper);
        }
        return beanWrapper;
    }

    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public MyBeanDefinitionReader getReader() {
        return reader;
    }
}
